/*
 * Load files *locally* (GeoJSON, KML, GPX) into the map
 * using the HTML5 File API.
 *
 * Requires Mapbox's togeojson.js to be in global scope
 * https://github.com/mapbox/togeojson
 * update by walt chiang
 * extend shpfile
 */
// window.GeoJSON = require('ol/format/GeoJSON');
import GeoJSON from "ol/format/GeoJSON";
(function (factory, window) {
    // define an AMD module that relies on 'leaflet'
    if (typeof define === 'function' && define.amd && window.toGeoJSON) {
        define(['leaflet'], function (L) {
            factory(L, window.toGeoJSON);
        });
    } else if (typeof module === 'object' && module.exports) {
        // require('LIBRARY') returns a factory that requires window to
        // build a LIBRARY instance, we normalize how we use modules
        // that require this pattern but the window provided is a noop
        // if it's defined
        module.exports = function (root, L, toGeoJSON) {
            if (L === undefined) {
                if (typeof window !== 'undefined') {
                    L = require('leaflet');
                } else {
                    L = require('leaflet')(root);
                }
            }
            if (toGeoJSON === undefined) {
                if (typeof window !== 'undefined') {
                    toGeoJSON = require('togeojson');
                } else {
                    toGeoJSON = require('togeojson')(root);
                }
            }
            factory(L, toGeoJSON,GeoJSON);
            return L;
        };
    } else if (typeof window !== 'undefined' && window.L && window.toGeoJSON) {
        factory(window.L, window.toGeoJSON);
    }
}(function fileLoaderFactory(L, toGeoJSON) {
  var FileLoader = L.Layer.extend({
    options: {
      layer: L.geoJson,
      layerOptions: {},
      fileSizeLimit: 1024
    },

    initialize: function (map, options) {
      this._map = map;
      L.Util.setOptions(this, options);

      this._parsers = {
        geojson: this._loadGeoJSON,
        json: this._loadGeoJSON,
        gpx: this._convertToGeoJSON,
        kml: this._convertToGeoJSON,
        shp: this._convertShpToGeoJSON
      };
    },

    load: function (file, ext) {
      var parser,
        reader;

      // Check file is defined
      if (this._isParameterMissing(file, 'file')) {
        return false;
      }

      // Check file size
      if (!this._isFileSizeOk(file.size)) {
        return false;
      }

      // Get parser for this data type
      parser = this._getParser(file.name, ext);
      if (!parser) {
        return false;
      }

      // Read selected file using HTML5 File API
      reader = new FileReader();
      reader.onload = L.Util.bind(function (e) {
        var layer;
        try {
          this.fire('data:loading', {filename: file.name, format: parser.ext});
          layer = parser.processor.call(this, e.target.result, parser.ext,file.name);
          if (layer)
            this.fire('data:loaded', {
              layer: layer,
              filename: file.name,
              format: parser.ext
            });
        } catch (err) {
          this.fire('data:error', {error: err});
        }
      }, this);
      // Testing trick: tests don't pass a real file,
      // but an object with file.testing set to true.
      // This object cannot be read by reader, just skip it.
      if (file.name.indexOf(".shp") > 0 || file.name.indexOf(".dbf") > 0) {
        reader.readAsArrayBuffer(file);
      }
      else if (!file.testing) {
        reader.readAsText(file);
      }
      // We return this to ease testing
      return reader;
    },
    loadShp: function (files, ext,encodingStr="utf-8") {
      var that = this;
      var layer;
      try {
        for (var i = 0; i < files.length; i++) {
          if (!files[i])
            continue;
          if (files[i].name.indexOf(".shp") > 0) {
            shpfile = files[i];
          }
          else if (files[i].name.indexOf(".dbf") > 0) {
            dbffile = files[i];
          }
          else if (files[i].name.indexOf(".prj") > 0) {
            prjfile = files[i];
          }
        }
        var shpfile, dbffile, prjfile;
        var shparray, dbfarray, priInfoTxt;
        var readerprj = new FileReader();//新建一个FileReader
        readerprj.readAsText(prjfile.raw || prjfile);//读取文件
        readerprj.onload = function (evt) { //读取完文件之后会回来这里
          priInfoTxt = evt.target.result;
        }
        var readershp = new FileReader();//新建一个FileReader
        readershp.readAsArrayBuffer(shpfile.raw || shpfile);//读取文件
        readershp.onload = function (evt) { //读取完文件之后会回来这里
          shparray = evt.target.result;
          var readerdbf = new FileReader();
          readerdbf.readAsArrayBuffer(dbffile.raw || dbffile);
          readerdbf.onload = function (evt) { //读取完文件之后会回来这里
            dbfarray = evt.target.result;
            shapefile.open(shparray, dbfarray, {encoding: encodingStr}).then(source => source.read()
              .then(function log(result) {
                if (result.done) return;
                // var feature = new GeoJSON().readFeature(result.value, {
                //   dataProjection: priInfoTxt,
                //   featureProjection: that._map.options.crs.code || 'EPSG:4326'
                // });//投影转换dataProjection geojson中featue的投影坐标，featureProjection 目标投影坐标系
                // feature.setStyle(style);
                // source1.addFeature(feature);
                // return source.read().then(log);
                layer = that.options.layer(result.value, that.options.layerOptions);

                if (layer.getLayers().length === 0) {
                  throw new Error('GeoJSON has no valid layers.');
                }

                if (that.options.addToMap) {
                  layer.addTo(that._map);
                }
                that.fire('data:loaded', {
                  layer: layer,
                  filename: shpfile.name,
                  format: ext
                });
                return layer;
              }))
              .catch(error => console.error(error.stack));
          }
        }
      } catch (err) {
        this.fire('data:error', {error: err});
      }
    },
    isShpFiles:function(files) {
      var fileExts = [];
      for (var i = 0; i < files.length; i++) {
        if (!files[i])
          continue;
        if (files[i].name.indexOf(".shp") > 0&&fileExts.concat(".shp")) {
          fileExts.push(".shp");
        }
        else if (files[i].name.indexOf(".dbf") > 0&&fileExts.concat(".dbf")) {
          fileExts.push(".dbf");
        }
        else if (files[i].name.indexOf(".prj") > 0&&fileExts.concat(".prj")) {
          fileExts.push(".prj");
        }
      }
      return fileExts.length > 1;
    },
    loadMultiple: function (files, ext) {
      var readers = [];
      if (files[0]) {
        files = Array.prototype.slice.apply(files);
        if (files.length > 1 && this.isShpFiles(files)) {
          readers.push(this.loadShp(files, ext));
        }
        else {
          while (files.length > 0) {
            readers.push(this.load(files.shift(), ext));
          }
        }
      }
      // return first reader (or false if no file),
      // which is also used for subsequent loadings
      return readers;
    },

    loadData: function (data, name, ext) {
      var parser;
      var layer;

      // Check required parameters
      if ((this._isParameterMissing(data, 'data'))
        || (this._isParameterMissing(name, 'name'))) {
        return;
      }

      // Check file size
      if (!this._isFileSizeOk(data.length)) {
        return;
      }

      // Get parser for this data type
      parser = this._getParser(name, ext);
      if (!parser) {
        return;
      }

      // Process data
      try {
        this.fire('data:loading', {filename: name, format: parser.ext});
        layer = parser.processor.call(this, data, parser.ext);
        if (layer)
          this.fire('data:loaded', {
            layer: layer,
            filename: name,
            format: parser.ext
          });
      } catch (err) {
        this.fire('data:error', {error: err});
      }
    },

    _isParameterMissing: function (v, vname) {
      if (typeof v === 'undefined') {
        this.fire('data:error', {
          error: new Error('Missing parameter: ' + vname)
        });
        return true;
      }
      return false;
    },

    _getParser: function (name, ext) {
      var parser;
      ext = ext || name.split('.').pop();
      parser = this._parsers[ext];
      if (!parser) {
        this.fire('data:error', {
          error: new Error('Unsupported file type (' + ext + ')')
        });
        return undefined;
      }
      return {
        processor: parser,
        ext: ext
      };
    },

    _isFileSizeOk: function (size) {
      var fileSize = (size / 1024).toFixed(4);
      if (fileSize > this.options.fileSizeLimit) {
        this.fire('data:error', {
          error: new Error(
            'File size exceeds limit (' +
            fileSize + ' > ' +
            this.options.fileSizeLimit + 'kb)'
          )
        });
        return false;
      }
      return true;
    },

    _loadGeoJSON: function _loadGeoJSON(content) {
      var layer;
      if (typeof content === 'string') {
        content = JSON.parse(content);
      }
      layer = this.options.layer(content, this.options.layerOptions);

      if (layer.getLayers().length === 0) {
        throw new Error('GeoJSON has no valid layers.');
      }

      if (this.options.addToMap) {
        layer.addTo(this._map);
      }
      return layer;
    },

    _convertToGeoJSON: function _convertToGeoJSON(content, format) {
      var geojson;
      // Format is either 'gpx' or 'kml'
      if (typeof content === 'string') {
        content = (new window.DOMParser()).parseFromString(content, 'text/xml');
      }
      geojson = toGeoJSON[format](content);
      return this._loadGeoJSON(geojson);
    },
    _convertShpToGeoJSON: function _convertShpToGeoJSON(content, format,filename) {
      var encodingStr="utf-8";
      var layer;
      var that=this;
      shapefile.open(content, null, {encoding: encodingStr}).then(source => source.read()
        .then(function log(result) {
          if (result.done) return;
          layer = that.options.layer(result.value, that.options.layerOptions);

          if (layer.getLayers().length === 0) {
            throw new Error('GeoJSON has no valid layers.');
          }

          if (that.options.addToMap) {
            layer.addTo(that._map);
          }
          that.fire('data:loaded', {
            layer: layer,
            filename: filename,
            format: format
          });
          return layer;
        }))
        .catch(error => console.error(error.stack));
      return null;
    }
  });

  var FileLayerLoad = L.Control.extend({
    statics: {
      TITLE: 'Load local file (GPX, KML, GeoJSON ,SHP)',
      LABEL: '&#8965;'
    },
    options: {
      position: 'topleft',
      fitBounds: true,
      layerOptions: {},
      addToMap: true,
      fileSizeLimit: 1024
    },

    initialize: function (options) {
      L.Util.setOptions(this, options);
      this.loader = null;
    },

    onAdd: function (map) {
      this.loader = L.FileLayer.fileLoader(map, this.options);

      this.loader.on('data:loaded', function (e) {
        // Fit bounds after loading
        if (this.options.fitBounds) {
          window.setTimeout(function () {
            map.fitBounds(e.layer.getBounds());
          }, 500);
        }
      }, this);

      // Initialize Drag-and-drop
      this._initDragAndDrop(map);

      // Initialize map control
      return this._initContainer();
    },

    _initDragAndDrop: function (map) {
      var callbackName;
      var thisLoader = this.loader;
      var dropbox = map._container;

      var callbacks = {
        dragenter: function () {
          map.scrollWheelZoom.disable();
        },
        dragleave: function () {
          map.scrollWheelZoom.enable();
        },
        dragover: function (e) {
          e.stopPropagation();
          e.preventDefault();
        },
        drop: function (e) {
          e.stopPropagation();
          e.preventDefault();

          thisLoader.loadMultiple(e.dataTransfer.files);
          map.scrollWheelZoom.enable();
        }
      };
      for (callbackName in callbacks) {
        if (Object.prototype.hasOwnProperty.call(callbacks,callbackName)) {
          dropbox.addEventListener(callbackName, callbacks[callbackName], false);
        }
      }
    },

    _initContainer: function () {
      var thisLoader = this.loader;

      // Create a button, and bind click on hidden file input
      var fileInput;
      var zoomName = 'leaflet-control-filelayer leaflet-control-zoom';
      var barName = 'leaflet-bar';
      var partName = barName + '-part';
      var container = L.DomUtil.create('div', zoomName + ' ' + barName);
      var link = L.DomUtil.create('a', zoomName + '-in ' + partName, container);
      link.innerHTML = L.Control.FileLayerLoad.LABEL;
      link.href = '#';
      link.title = L.Control.FileLayerLoad.TITLE;

      // Create an invisible file input
      fileInput = L.DomUtil.create('input', 'hidden', container);
      fileInput.type = 'file';
      fileInput.multiple = 'multiple';
      if (!this.options.formats) {
        fileInput.accept = '.gpx,.kml,.json,.geojson,.shp,.dbf,.prj';
      } else {
        fileInput.accept = this.options.formats.join(',');
      }
      fileInput.style.display = 'none';
      // Load on file change
      fileInput.addEventListener('change', function () {
        thisLoader.loadMultiple(this.files);
        // reset so that the user can upload the same file again if they want to
        this.value = '';
      }, false);

      L.DomEvent.disableClickPropagation(container);
      L.DomEvent.on(link, 'click', function (e) {
        fileInput.click();
        e.preventDefault();
      });
      return container;
    }
  });

  L.FileLayer = {};
  L.FileLayer.FileLoader = FileLoader;
  L.FileLayer.fileLoader = function (map, options) {
    return new L.FileLayer.FileLoader(map, options);
  };

  L.Control.FileLayerLoad = FileLayerLoad;
  L.Control.fileLayerLoad = function (options) {
    return new L.Control.FileLayerLoad(options);
  };
}, window));
